# -*- coding: UTF-8 -*-
# 设置窗口
import os
import re
import shutil
import stat
import threading
import time
from enum import Enum

from PyQt5.QtCore import QFile, QTextStream, QPoint, QSize, QStringListModel
from PyQt5.QtWidgets import *

from command.shell import Command
from main.config_widget import config_set
from tool.comm import Tool
from window.confirm_information import Ui_About_Dialog
from window.main_window import Ui_main_window

__is_dev = True


# 消息类型
class Message(Enum):
    Error = "#F56C6C"
    Success = "#67C23A"
    Primary = "#409EFF"
    Default = "#303133"
    Warning = "#E6A23C"


if __is_dev:
    base_path = "./main"
else:
    base_path = "../main"

global_path = base_path + "/config/home.config"
style_path = base_path + "/style/style.qss"


class set_widget(Ui_main_window, QMainWindow):
    __tag__ = False
    __pro_max = 692

    def __init__(self):
        super(set_widget, self).__init__()
        self.branch_name = None
        self.branch_name01 = None
        self.setupUi(self)
        self.show()
        self.tab_index = 1
        self.bind_event()
        file = QFile(style_path)
        self.add_input_value = None
        self.data = set()
        self.data01 = set()
        self.row = 0
        self.checkbox_clear = False
        self.progress_visible(False)  # 先隐藏进度条
        if file.exists():
            file.open(QFile.ReadOnly | QFile.Text)
            stream = QTextStream(file)
            self.setStyleSheet(stream.readAll())
        else:
            print("样式表不存在")
        self.about_dialog = about_dialog()
        self.home_path_val = None  # 根目录 用来传文件夹的
        self.path_tag = False
        self.cmd = Command()

    def progress_visible(self, tag: bool):
        height = 472  # 最大高度
        if tag is False:  # 隐藏进度条
            height = 442  # 窗口拉伸到 442
        self.setFixedHeight(height)
        self.txt_progress.setHidden(not tag)
        self.icon_progress.setHidden(not tag)
        self.line.setHidden(not tag)
        self.icon_grass.setHidden(not tag)

    def show_path_dialog(self):
        box = config_set()
        box.finished.connect(lambda: self.svn_origin.setText(Tool.get_config_by_key("svn_path")))
        box.exec_()

    def bind_event(self):
        self.sv_target.setText(Tool.get_kv("svn_target"))
        self.set_menu_item_path.triggered.connect(self.show_path_dialog)
        self.tab01.clicked.connect(lambda: self.show_index(0))
        self.tab02.clicked.connect(lambda: self.show_index(1))
        self.set_menu_item_clear_cache.triggered.connect(self.clear_cache)
        self.exit_btn.triggered.connect(self.close)
        self.input_add_item.textEdited.connect(self.item_add_input)
        self.svn_origin.setText(Tool.get_config_by_key("svn_path"))
        self.btn_add_item.clicked.connect(self.push_node)
        self.start_merge.clicked.connect(self.merge)
        self.list_of_canvas.itemChanged.connect(lambda: self.list_of_canvas_empty())
        self.sv_target.textChanged.connect(lambda a: (Tool.set_kv("svn_target", a), self.input_svn_target.setText(a)))
        self.btn_about.triggered.connect(lambda: self.about_dialog.visible(True))
        self.auto_clear_cache.toggled.connect(lambda v: self.set_clear_fun(v))

    def set_clear_fun(self, val: bool):
        self.checkbox_clear = val

    # 绑定第二页的
    def bind_event01(self):
        self.input_svn_target.setText(Tool.get_kv("svn_target"))
        self.btn_select_file.clicked.connect(self.select_file_handler)
        self.btn_start_merge.clicked.connect(self.merge01)
        self.file_list.setDragEnabled(False)
        v_bar = self.file_list.verticalScrollBar()
        v_bar.setSingleStep(1)  # 平滑滚动
        home_path = Tool.get_kv("home_path", file_path=global_path)  # 获取缓存中home_path
        if Tool.str_not_empty(home_path):
            self.home_path_val = home_path
            self.home_path.setText(home_path)
        else:
            Tool.set_kv("home_path", None, file_path=global_path)
        self.home_path.textChanged.connect(lambda v: self.set_home_path(v))
        self.btn_set_home_path.clicked.connect(self.get_home_path)

    def get_home_path(self):
        _dir = QFileDialog.getExistingDirectory()
        if Tool.str_not_empty(_dir):
            self.set_home_path(_dir)

    def set_home_path(self, val: str):
        self.home_path_val = val
        Tool.set_kv("home_path", val,
                    file_path=global_path)
        self.home_path.setText(val)

    # 设置相关控件的可用性
    def set_enable(self, enable: bool):
        self.home_path.setEnabled(enable)
        self.btn_set_home_path.setEnabled(enable)
        self.btn_select_file.setEnabled(enable)
        self.input_svn_target.setEnabled(enable)

    def select_file_handler(self):
        files = QFileDialog.getOpenFileNames(parent=self, caption="请选择文件", directory=self.home_path_val)[0]
        if len(files) > 0:
            for file in files:
                match = re.search(self.home_path_val, file)
                if bool(match) is False:
                    self.show_msg("请选择正确的ui路径", Message.Error)
                    break
                elif self.path_tag is False:
                    self.path_tag = True
                self.data01.add(file)
            self.render_to_list(files)

    def render_to_list(self, _list):
        if _list is not None and len(_list) > 0:
            str_item = QStringListModel(_list)
            self.file_list.setModel(str_item)

    # 本地到远程合并
    def merge01(self):
        if self.path_tag is False:
            self.show_msg("禁止提交，请检查", Message.Error, duration=3)
            return
        url = self.input_svn_target.text()
        msg = self.svn_msg.toPlainText()
        if len(self.data01) == 0:
            self.show_msg("列表为空，没有传输任务", Message.Error, duration=2)
        elif Tool.str_is_empty(msg) and msg.find("refs") == -1:
            self.show_msg("日志格式错误：例如 refs xxxxx", Message.Error, duration=2)
        elif Tool.str_not_empty(url) and Tool.re_is_url(url):
            self.set_enable(False)
            self.set_progress(0)
            self.show_msg("任务开始", Message.Success)  # 创建本地分支
            root_path = Tool.get_config_by_key("work_path")
            idx = -url.rfind("/") - 1
            branch_name = url[-idx:]  # 分支名字
            if root_path[-1] == "/":
                root_path = root_path
            else:
                root_path = root_path + "/"
            path = root_path + branch_name
            if os.path.exists(path):  # 创建本地分支，如果存在就删除之后再创建 防止
                shutil.rmtree(path, onerror=self.modify_permission)
                os.mkdir(path)
            # 拉取远程文件
            try:
                self.progress_visible(True)
                threading.Thread(target=lambda: self.cmd.checkout(url, path, self.data01,
                                                                  lambda x, b, c, d: self.pull_progress(x, b, c, d),
                                                                  lambda e, d, f: self.finish01(e, d, path))).start()
                self.set_progress(25)
            except NotADirectoryError:
                self.progress_visible(False)
                self.show_msg(f"远程仓库连接失败{url}", Message.Error)
                return
            self.set_enable(True)
        else:
            self.show_msg("svn 路径不合法", Message.Error, duration=2)

    # 完成后
    def finish01(self, a, b, c):
        self.set_progress(30)
        for file in self.data01:  # 拷贝目录和文件 # 完成后将外部文件拷贝过来覆盖
            res = self.is_root_path(self.home_path_val, file)  # 拷贝文件
            if res[0] == 1:
                shutil.copy2(file, c)
            elif res[0] == 0:
                self_path = c + "/" + res[2]
                os.makedirs(self_path, exist_ok=True)
                shutil.copy2(file, self_path)  # 拷贝文件到
        self.set_progress(45)
        status_list = self.cmd.status(c)
        Tool.text_line_map(status_list, lambda x, y: self.add_file(c, x, y))
        self.show_msg("本地化任务完成", Message.Success)
        time.sleep(1.6)  # 暂停一下任务开始提交
        self.show_msg("开始合并", Message.Success)
        self.cmd.commit(path=c, msg=self.svn_msg.toPlainText())
        self.set_progress(100)
        self.show_msg(f"合并完成:{len(self.data01)}条", Message.Success, duration=2.5)
        self.row = 0  # 指针归零
        self.progress_visible(False)

    # 和home path 对比，判断是否是根路径，不是的话就创建文件夹并创建文件 a home_path b real_path 返回值 true:1;false:0;err:-1
    def is_root_path(self, a: str, b: str):
        print(a, b)
        if Tool.str_not_empty(a) and Tool.str_not_empty(b):
            real_path = b.replace(a + "/", "", 1)  # 去掉文件路径的根路径
            path = os.path.dirname(real_path)
            file_name = os.path.basename(real_path)
            if Tool.str_is_empty(path):  # 判断是否在根目录
                return [1, file_name]
            else:
                return [0, file_name, path]
        else:
            return [-1]

    def list_of_canvas_empty(self):
        if self.list_of_canvas.count() == 0:
            pass

    def merge(self):  # 先下载对应的csd和csb 到本地
        svn_url = self.svn_origin.text()
        svn_merge_url = self.sv_target.text()
        log_content = self.svn_msg.toPlainText()
        if Tool.re_is_url(svn_url) and Tool.re_is_url(svn_merge_url):
            if self.data.__len__() == 0:
                self.show_msg("当前搬运列表为空")
            elif Tool.str_not_empty(svn_url) and Tool.str_not_empty(svn_merge_url):
                if Tool.str_not_empty(log_content) and log_content.find("refs") != -1:
                    try:
                        threading.Thread(target=lambda: self.fetch(svn_url, svn_merge_url)).start()
                    except AttributeError as err:
                        self.show_msg("merge异常", Message.Error, duration=2)
                        pass
                else:
                    self.show_msg("日志错误 refs xxxx", Message.Error)
            else:
                self.show_msg("信息填写错误，请检查", Message.Error)
        else:
            self.show_msg("仓库地址不合法", Message.Error)

    def set_progress(self, pro: int):  # 设置进度条
        try:
            QApplication.processEvents()  # 允许GUI更新
            val = int((pro / 100) * 632) + 60
            self.icon_progress.setGeometry(val, 381, 28, 28)
        except RuntimeError as e:
            pass

    def fetch(self, url01, url02):  # 判断提交log
        self.show_msg("······", Message.Default)
        local_path = Tool.get_config_by_key("work_path")
        idx = -url01.rfind("/") - 1
        idx01 = -url02.rfind("/") - 1
        self.branch_name = url01[-idx:]  # 分支名字
        self.branch_name01 = url02[-idx01:]  # 分支名字
        if self.branch_name.casefold() == self.branch_name01.casefold():  # 解决分支名字一样的情况
            self.branch_name01 = self.branch_name01 + "01"

        if local_path[-1] == "/":
            local_branch_path = local_path
        else:
            local_branch_path = local_path + "/"
        if not os.path.exists(local_branch_path):
            self.show_msg(f"创建路径{local_branch_path}")
            try:
                os.mkdir(local_branch_path)
                time.sleep(1 / 2)  # 暂停0.5s
            except FileNotFoundError as err:
                self.show_msg(f"本地路径配置错误:{local_branch_path} err:{err}", Message.Error, duration=3)
                return
        self.progress_visible(True)
        self.set_progress(0)  # 进度
        self.show_msg("开始任务", Message.Default)
        data = set()  # 需要把文件拆分为csd 和csb
        for f in self.data:
            csd = f + ".csd"
            csb = f + ".csb"
            data.add(csd)
            data.add(csb)
        try:
            self.cmd.checkout(url01, local_branch_path + self.branch_name, data,
                              None,
                              None)  # a 分支
            self.set_progress(25)  # 进度
        except NotADirectoryError as err:
            self.show_msg(f"远程仓库连接失败:{url01}", Message.Error)
            self.set_progress(False)
            return
        try:
            self.cmd.checkout(url02, local_branch_path + self.branch_name01, data,
                              lambda x, b, c, d: self.pull_progress(x, b, c, d),
                              lambda e, d, f: self.finish(e, d, f))  # b 分支
        except NotADirectoryError as err:
            self.show_msg(f"远程仓库连接失败:{url02}", Message.Error)
            self.set_progress(False)

    def add_file(self, path, a, b):
        if a == "?":  # 判断不在库中就 svn add
            self.cmd.add(path + "\\" + b)

    # TODO 完成拉取 then 提交到b仓库
    def finish(self, a0, a1, a2):
        self.set_progress(50)
        self.row = 0  # 重置计数器为0
        root_path = Tool.get_config_by_key("work_path") + "/"
        path_a = root_path + self.branch_name
        path_b = root_path + self.branch_name01  # 远程分支
        shutil.copytree(src=path_a, dst=path_b, ignore=shutil.ignore_patterns(".svn"), dirs_exist_ok=True)
        res = self.cmd.status(path_b)
        self.set_progress(65)
        Tool.text_line_map(res, lambda a, b: self.add_file(path_b, a, b))
        msg = self.svn_msg.toPlainText()
        # print("中断提交")
        # return
        try:  # 检测仓库是否上锁
            res_commit = self.cmd.commit(path_b, msg)
            self.set_progress(75)
            Tool.text_line_map(res_commit, lambda a, b: self.filter_file_name(a, b))
            self.show_msg("正在合并", Message.Success)
        except Exception as e:
            self.show_msg("提交错误{}".format(e), Message.Error)
        finally:
            self.set_progress(100)
            time.sleep(0.2)
            self.progress_visible(False)
        if self.checkbox_clear is True:
            self.clear_cache()
        self.show_msg(f"合并成功：{int(self.row / 2)}条", Message.Success)
        self.row = 0

    # TODO 过滤文件计数器
    def filter_file_name(self, n, a):
        file_name = os.path.basename(a)
        if file_name.find(".csd") != -1 or file_name.find(".csb") != -1:
            self.row += 1

        # TODO 待做 进度监控

    def pull_progress(self, a, b, c, d):
        self.show_msg(f"任务队列:{c}/{d}", Message.Default)

    def item_add_input(self, text):
        txt = text.strip()
        if txt.find(".csd") != -1:
            txt = re.sub(".csd", "", txt)
        elif txt.find(".csb") != -1:
            txt = re.sub(".csb", "", txt)
        self.add_input_value = txt
        # 修改删除失败的权限

    @staticmethod
    def modify_permission(func, path, execinfo):
        os.chmod(path, stat.S_IWUSR)
        func(path)

    # TODO 清除本地缓存
    def clear_cache(self):
        _path = Tool.get_config_by_key("work_path")
        if os.path.exists(_path):
            try:
                self.show_msg("清除缓存中...", Message.Default, duration=1.65)
                shutil.rmtree(_path, onerror=self.modify_permission)  # 删除文件夹下的所有内容
            except OSError as e:
                pass
            finally:
                self.show_msg("暂无消息", Message.Default)
        else:
            self.show_msg(f"路径{_path}不存在", Message.Error, duration=1.5)

    def show_index(self, index):
        QApplication.processEvents()  # 允许GUI更新
        if index == 0:  # 第一个选中时
            Tool.animate(self.svn_msg, None, QPoint(410, 62), 270, self)
            self.tab01.setStyleSheet("QToolButton#tab01{background-color:#79bbff;}")
            self.tab02.setStyleSheet("QToolButton#tab02{background-color:#409EFF;}")
        elif index == 1:  # 第二个选中时
            self.tab02.setStyleSheet("QToolButton#tab02{background-color:#79bbff;}")
            self.tab01.setStyleSheet("QToolButton#tab01{background-color:#409EFF;}")
            Tool.animate(self.svn_msg, None, QPoint(10, 73), 270, self)
        Tool.animate(self.slider, None, QPoint(-740 * index, 0), 270, self)
        if self.__tag__ is False:  # 作为初始化第二页开关
            self.__tag__ = True
            self.bind_event01()

    # TODO 显示消息提示文字
    def show_msg(self, content=None, _type=Message.Error, duration=0.0):
        if content is not None:
            self.error_tips.setHidden(False)
            self.error_tips.setStyleSheet("QLabel#error_tips{color:" + _type.value + ";}")
            self.error_tips.setText(content)
            if duration > 0:
                threading.Thread(
                    target=lambda: (time.sleep(duration), self.error_tips.setText(""))).start()

    def push_node(self):
        content = self.add_input_value
        if content is None:
            self.show_msg("输入内容为空")
        elif content in self.data:
            self.show_msg("文件已存在")
        else:
            item = QListWidgetItem(self.list_of_canvas)
            size = QSize()
            size.setHeight(26)
            size.setWidth(280)
            item.setSizeHint(size)  # 不设置看不到
            node = QWidget()
            qv = QHBoxLayout()
            node.setStyleSheet("QWidget{background:transparent;}")
            qbtn = QPushButton()
            qbtn.clicked.connect(lambda: self.drop_node(content, item))
            qbtn.setText("×")
            qbtn.setObjectName("btn")

            qv.addWidget(QLabel(content))
            qv.addWidget(qbtn)

            node.setLayout(qv)
            self.list_of_canvas.setItemWidget(item, node)
            self.list_of_canvas.scrollToBottom()  # 滚动到底部
            self.data.add(content)

            self.input_add_item.setText("")

    def drop_node(self, ctx, item):
        # self.list_of_canvas.setCurrentRow(item)
        current_index = self.list_of_canvas.currentRow()
        if current_index > -1:
            self.list_of_canvas.takeItem(current_index)
            self.data.remove(ctx)

    def clear(self):
        try:
            self.list_of_canvas.clear()  # 清空列表
            self.data.clear()  # 清除传输列表
        except RuntimeError:
            pass


# 关于弹窗
class about_dialog(Ui_About_Dialog, QDialog):
    def __init__(self):
        super(about_dialog, self).__init__()
        self.setupUi(self)
        self.setModal(True)
        self.btn_close.clicked.connect(lambda: self.visible(False))  # 关闭按钮

    def visible(self, tag=False):
        if tag:
            self.exec_()
        else:
            self.close()
